using System;
using Microsoft.SPOT.Hardware;

namespace FEZ_Components
{
  public class RTC_DS1307 : IDisposable
  {
    private I2CDevice I2C;
    I2CDevice.I2CTransaction[] xaction;
    ushort DS1307_Address = 0x68;

    public enum SQWE
    {
      Pulse_1Hz = 0x10,
      Pulse_4096kHz = 0x16,
      Pulse_8192kHz = 0x20,
      Pulse_32768kHz = 0x26
    }



    public void Dispose()
    {
      I2C.Dispose();
      xaction = null;
    }

    public RTC_DS1307()
    {
      //Create I2C object
      I2CDevice.Configuration conf = new I2CDevice.Configuration(DS1307_Address, 100);
      I2C = new I2CDevice(conf);
    }

    public  DateTime GetDateTime()
    {
      xaction = new I2CDevice.I2CTransaction[2];
      xaction[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0x00 });
      byte[] ReturnedDateTime = new byte[7];
      xaction[1] = I2CDevice.CreateReadTransaction(ReturnedDateTime);
      if (I2C.Execute(xaction, 1000) == 0)
      {
        new Exception("Failed to send I2C data");
      }

      int sec = bcdToDec(ReturnedDateTime[0]) & 0x7f;
      int min = bcdToDec(ReturnedDateTime[1]);
      int hour = bcdToDec(ReturnedDateTime[2]) & 0x3f;
      int dayofweek = bcdToDec(ReturnedDateTime[3]);
      int dayofmonth = bcdToDec(ReturnedDateTime[4]);
      int month = bcdToDec(ReturnedDateTime[5]);
      int year = bcdToDec(ReturnedDateTime[6]) + 2000;

      DateTime dt = new DateTime(year, month, dayofmonth, hour, min, sec);
      return dt;
    }

    public void SetDateTime(int year, int month, int day, int dow, int hour, int minute, int second)
    {
      xaction = new I2CDevice.I2CWriteTransaction[1];
      byte[] sb = new byte[8] { 0x00, 
                                  decToBcd(second), 
                                  decToBcd(minute), 
                                  decToBcd(hour), 
                                  decToBcd(dow), 
                                  decToBcd(day), 
                                  decToBcd(month), 
                                  decToBcd(year - 2000)  
                                };

      xaction[0] = I2CDevice.CreateWriteTransaction(sb);
      int x = I2C.Execute(xaction, 1000);
      if (x == 0)
      {
        new Exception("Failed to send I2C data");
      }
    }

    public void SetSQWE(SQWE sqwe)
    {
      xaction = new I2CDevice.I2CWriteTransaction[1];
      byte[] sb = new byte[2] { 0x07, 
                                (byte)sqwe
  
                                };

      xaction[0] = I2CDevice.CreateWriteTransaction(sb);
      if (I2C.Execute(xaction, 1000) == 0)
      {
        new Exception("Failed to send I2C data");
      }
    }

    public void SaveData(int eled, int elen, int gas, int wat)
    {
      xaction = new I2CDevice.I2CWriteTransaction[1];
      byte[] sb = new byte[17] { 0x08, 
                                WriteInt32(eled)[0],
                                WriteInt32(eled)[1],
                                WriteInt32(eled)[2],
                                WriteInt32(eled)[3],
                                WriteInt32(elen)[0],
                                WriteInt32(elen)[1],
                                WriteInt32(elen)[2],
                                WriteInt32(elen)[3],
                                WriteInt32(gas)[0],
                                WriteInt32(gas)[1],
                                WriteInt32(gas)[2],
                                WriteInt32(gas)[3],
                                WriteInt32(wat)[0],
                                WriteInt32(wat)[1],
                                WriteInt32(wat)[2],
                                WriteInt32(wat)[3]
                                };

      xaction[0] = I2CDevice.CreateWriteTransaction(sb);
      if (I2C.Execute(xaction, 1000) == 0)
      {
        new Exception("Failed to send I2C data");
      }
      
    
    }

    public int[] ReadData()
    {

      xaction = new I2CDevice.I2CTransaction[2];
      xaction[0] = I2CDevice.CreateWriteTransaction(new byte[] { 0x08 });
      byte[] RetCounters = new byte[16];
      xaction[1] = I2CDevice.CreateReadTransaction(RetCounters);
      if (I2C.Execute(xaction, 1000) == 0)
      {
        new Exception("Failed to send I2C data");
      }
      byte[] eled = new byte[4] {RetCounters[0], RetCounters[1], RetCounters[2], RetCounters[3]};
      byte[] elen = new byte[4] { RetCounters[4], RetCounters[5], RetCounters[6], RetCounters[7] };
      byte[] gas = new byte[4] { RetCounters[8], RetCounters[9], RetCounters[10], RetCounters[11] };
      byte[] wat = new byte[4] { RetCounters[12], RetCounters[13], RetCounters[14], RetCounters[15] };

      int[] vals = new int[4] { ReadInt32(eled, 0), ReadInt32(elen, 0), ReadInt32(gas, 0), ReadInt32(wat, 0)  };


     return vals;

    }

    public bool NVRamWrite(byte[] bytes, byte startaddress)
    {
      xaction = new I2CDevice.I2CWriteTransaction[1];
      byte[] sb = new byte[bytes.Length + 1];
      bytes.CopyTo(sb, 1);
      sb[0] = startaddress;
      xaction[0] = I2CDevice.CreateWriteTransaction(sb);
      return (I2C.Execute(xaction, 1000) == 0) ? false : true;
    }

    public byte[] NVRamRead(byte startaddress, int count)
    {
      xaction = new I2CDevice.I2CTransaction[2];
      xaction[0] = I2CDevice.CreateWriteTransaction(new byte[] { startaddress });
      byte[] ReadBytes = new byte[count];
      xaction[1] = I2CDevice.CreateReadTransaction(ReadBytes);
      if (I2C.Execute(xaction, 1000) == 0)
      {
        new Exception("Failed to read I2C data");
      }
      return ReadBytes;
    }

    private byte decToBcd(int val)
    {
      return (byte)((val / 10 * 16) + (val % 10));
    }

    private byte bcdToDec(byte val)
    {
      return (byte)((val / 16 * 10) + (val % 16));
    }

    private static byte[] WriteInt32(int value)
    {
      byte[] result = new byte[4];
      result[0] = (byte)((value & 0xFF000000) >> 24);
      result[1] = (byte)((value & 0x00FF0000) >> 16);
      result[2] = (byte)((value & 0x0000FF00) >> 8);
      result[3] = (byte)((value & 0x000000FF));

      return result;
    }

    public static int ReadInt32(byte[] buf, int offset)
    {
      return (buf[offset + 0] << 24) + (buf[offset + 1] << 16) + (buf[offset + 2] << 8) + (buf[offset + 3]);
    }


  }
}